import json
import os.path
import random
from collections import Counter
from pathlib import Path

import dgl
import numpy as np
import pandas as pd
import torch
from numpy import ndarray
from pandas import DataFrame
from sympy import false, true
from torch import tensor

def list_array2tesnor(list_of_arrays):
    """功能：[array]-》转换为tensor；
    问题：[array]->直接tensor化非常慢，
    方法：先[array]->np.array([array])-》再tensor化
    """
    # Convert the list to a single numpy array
    array = np.array(list_of_arrays)
    # array = np.stack(list_of_arrays)#对同形状的可以用堆叠
    # Convert the numpy array to a PyTorch tensor
    tensor1 = torch.from_numpy(array)#tensor = torch.from_numpy(array).type(torch.float64)指定数据类型
    return tensor1



def list_random_sample(examples_len_set,num_subsets):
    """功能：随机采样-从list采样指定大小的子集；
    调用：
    examples_len_set=range(0,10)
    subset,examples_set_remain=list_random_sample(examples_len_set,2)
    返回：
    subset:[]
    examples_set_remain:[]
    注：由集合随机采样较慢，已被废弃；
    """
    subset=np.random.choice(examples_len_set,num_subsets)#[]->[],随机采样-指定大小的子集
    # print(subset)
    #list求差集：先转为集合
    examples_set_remain=list(set(examples_len_set).difference(subset))#前-后
    # print(examples_len_set)
    return subset,examples_set_remain
# print(random.sample({0,1,2},2))

def dict2DataFrame(count_true:dict,col_key='true',pic_show=true):
    """
    功能：dict->DataFrame,并绘制饼状图；
    输入：{}形如{'qualifier value': 37801, 'body structure': 15998}
    参数：
        col_key='true'  图标题-列名
        pic_show=true   是否绘制饼状图
    饼状图数据形式：
        {'列名':[float数据]},index=[‘行名‘]
        df = pd.DataFrame({'mass': [0.330, 4.87 , 5.97],
            'radius': [2439.7, 6051.8, 6378.1]},index=['Mercury', 'Venus', 'Earth'])
            即一列一个饼图（列值为百分比），index行名-多个标记；
    思路：
        数据行：dict的key作为行名
        数据列：{col_key：dict的values}
    返回：
        df  pd.DataFrame-适合饼状图
        col_value-饼状图的y值-图标题
        indexname-饼状图的行名-图分类字段
    调用1：
        from collections import Counter
        count_true = Counter({'qualifier value': 37801, 'body structure': 15998})
        count_true = dict(count_true.most_common(10))  # Counter取前n项-返回[(键,数量值),]
        df = dict2DataFrame(count_true)
    调用2：

    """
    # count_true=dict(count_true.most_common(5))#Counter取前n项-返回[(键,数量值),]
    # print(count_true)
    from matplotlib import pyplot as plt
    indexname=[]
    col_value=[]
    col_dict={}
    for k,v in count_true.items():
        indexname.append(k)
        col_value.append(v)
    col_dict= {col_key:col_value}
    # print(indexname,type(indexname))
    # print(col_value,type(col_value))
    # print(col_dict,type(col_dict))
    df = pd.DataFrame(col_dict,index=indexname)
    if pic_show:
        plot = df.plot.pie(y=col_key, figsize=(10,10), autopct='%1.0f%%')#绘制饼状图-带占比（百分比）
        plt.show()
    return df,col_value,indexname


def str_getin_bracket(str1:str,num:int=1):
    """
    功能：提取字符串括号里的内容-取前num个-正则表达式；str->list[str]
    参数：num:保留元素个数；
    返回：list[str]；如['123']
    调用：
    str1="Oil of Geranium (substance)(substance)"
    print(str_getin_bracket(str1))#['substance']
    """
    import re
    result_list = re.findall(r"[(](.*?)[)]",str1)#-#
    # result_list = re.findall(r"[(](.*?)[)]", str1)  # -#
    result_list=result_list[:num]#取前num个
    return result_list


def str_repl_2list(str1:str,num:int=1):
    """
    功能：字典式字符串分别放入2个list(指定num个元素);替换一些字符；分隔；
    字典式字符串："k:v"；如"{\"The Conversation\": 0.65, \"Hannibal\": 0.636, \"Gone Baby Gone\": 0.607}"
    形式变化："k:v"->[k],[v]
    参数：num: 取指定个数的list
    调用：
    str1= "{\"Naruto the Movie: Legend of the Stone of Gelel\": 0.828, \"Evangelion 1.11: You Are (Not) Alone\": 0.816}"
    key,value=str_repl_2list(str1,num=1)
    print(f"{key}\n{value}")#['Naruto the Movie: Legend of the Stone of Gelel']    ['0.828']
    """
    str1 = str1.split(",")  # 每个元素会加上单引号;str->list['k:v']#
    # print(str1)#['{"Bleach: Memories of Nobody": 0.842', ' "Bleach: The DiamondDust Rebellion": 0.842',
    key = []
    value = []
    for i in str1:
        str1 = i.replace("\\", "").replace("{", "").replace("}", "").replace('"', '').replace("'", '')#替换
        pos = str1.rfind(':')  # 查找最右冒号位置
        str_left = str1[:pos].strip()#去除空格
        str_right = str1[pos + 1:].strip()
        key.append(str_left)
        value.append(str_right)
    key = key[:num]  # 取指定个数的list
    value = value[:num]

    return key,value


def list_print(supports:list):
    """功能：查看list中元素形状"""
    if type(supports)==list:#长度为1的list
        for i, v in enumerate(supports):
            if type(v) in [list, dict, tuple, str]:  # len()形状类型
                print(f"第{i}元为:{type(v)},len={len(v)}")
            else:
                print(f"第{i}元为:{type(v)},shape={v.shape}")


def list_count(temp:list):
    """
    功能：list元素计数-collections.Counter得到dict{key:count值}
    返回：dict;-{值：数量}-如{'1': 1, 2: 2, 1: 1, 3: 3, 4: 1, 6: 1}
    调用：
    temp = ['1', 2, 1, 3, 4, 2, 3, 6, 3]
    list_count(temp)
    """
    from collections import Counter
    ct=Counter(temp)#不同元素计数#Counter({3: 3, 2: 2, '1': 1, 1: 1, 4: 1, 6: 1})-{值：数量}
    # print(len(ct))#种类数
    # sum=0#总元素个数
    # for u,v in ct.items():
    #     sum=sum+v
    #     print(f"{u}:{v}个")
    return ct #dict(ct)


def mkdir(dirname):
    """
    功能：创建目录；
    调用：
    mkpath = "data/"
    mkdir(mkpath)
    """
    # 引入模块
    import os
    # 去除首位空格
    dirname = dirname.strip()
    # 去除尾部 \ 符号
    dirname = dirname.rstrip("\\")
    if dirname=="":
        return False
    # 判断路径是否存在
    # 存在     True
    # 不存在   False
    isExists = os.path.exists(dirname)
    # 判断结果
    if not isExists:
        # 如果不存在则创建目录
        # 　  # 创建目录操作函数
        os.makedirs(dirname)
        print(dirname + ' 创建成功')
        return True
    else:
        # 如果目录存在则不创建，并提示目录已存在
        print(dirname + ' 目录已存在,不再重复创建！')
        return False


def mkfile(dirname,filename):
    """
    功能：创建文件-指定目录；
    return:文件对象
    调用：
        dirname="data"#目录名
        filename="dict.txt"#文件名
    """
    # mkdir(dirname)  # 创建目录
    import os
    # 去除首位空格
    dirname = dirname.strip()
    # 去除尾部 \ 符号
    dirname = dirname.rstrip("\\")
    if dirname == "":
        return False
    # 判断路径是否存在
    # 存在     True
    # 不存在   False
    isExists = os.path.exists(dirname)
    # 判断结果
    if not isExists:
        # 如果不存在则创建目录
        # 　  # 创建目录操作函数
        os.makedirs(dirname)
        print(dirname + ' 创建成功')
        return True
    else:
        # 如果目录存在则不创建，并提示目录已存在
        print(dirname + ' 目录已存在,不再重复创建！')

    save_dir = Path(__file__).parent.absolute()  # 当前所在目录
    fdir = os.path.join(save_dir, dirname, filename)  # 连接目录
    file = open(fdir, 'w')
    print(fdir, "创建成功")
    file.close()
    return file

	
def listuple2tensor(train3:list,devicename = 'cuda'):
    """
    功能：list[(s,r,o)]->x,y   tensor-torch.float32
    Parameters
    ----------
        train3:list[(s,r,o),..]形式
        devicename='cuda'#'cpu'
    Returns
        x-torch.Size([n, 2]),y-torch.Size([n, 1])
    调用：
        train3 = run_3num.data['train']  # list[(s,r,o)]形式
        x,y=listuple2tensor(train3) #GCN输入x的要求-tensor-torch.float32
    -------
    """
    # train3 = run_3num.data['train']  # list[(s,r,o)]形式
    train3 = tensor(train3)  # torch.Size([97, 3])-训练数据
    train3 = train3.type(torch.float)  # 改变数据类型torch.Size([97, 3]) torch.float32
    # print(train3.shape, train3.dtype)
    x = train3[:, :train3.shape[1]-1].to(devicename)  # torch.Size([97, 2])
    y = train3[:, train3.shape[1]-1].to(devicename)  # torch.Size([97])
    return x,y
	
	
def dict2txt(dictname,dirname,filename,splstr='\t'):
    """
    功能：dict->txt（字典存入文本文件）
    输出：文本文件（在当前目录下创建）
    返回：文件对象
    调用：
    dict_temp={'001' : 'a',  '002' : 'b', '003 ': 'c'}
    dirname="data"#目录名
    filename="dict.txt"#文件名
    splstr='\t'#分隔符名
    dict2txt(dict_temp,dirname,filename,splstr)
    """
    # 先创建并打开一个文本文件
    # file=mkfile(dirname,filename)
    import os
    # 去除首位空格
    dirname = dirname.strip()
    # 去除尾部 \ 符号
    dirname = dirname.rstrip("\\")
    if dirname == "":
        return False
    # 判断路径是否存在
    # 存在     True
    # 不存在   False
    isExists = os.path.exists(dirname)
    # 判断结果
    if not isExists:
        # 如果不存在则创建目录
        # 　  # 创建目录操作函数
        os.makedirs(dirname)
        print(dirname + ' 创建成功')
        return True
    else:
        # 如果目录存在则不创建，并提示目录已存在
        print(dirname + ' 目录已存在,不再重复创建！')

    save_dir = Path(__file__).parent.absolute()  # 当前所在目录
    fdir = os.path.join(save_dir, dirname, filename)  # 连接目录
    file = open(fdir, 'w')
    print(fdir, "创建成功")

    # 遍历字典的元素，将每项元素的key和value分拆组成字符串，注意添加分隔符和换行符
    for k, v in dictname.items():
        file.write(str(k) + splstr+ str(v) + '\n')
    # 注意关闭文件
    file.close()
    return file


def list_tuple2xy(data):
    """功能：数据处理为特征x与标签y；从每个元组中获取前两个元素作为x，最后一个元素作为y；
    形式：[()]->x,y
    调用：
    data = [(363608, 353807, 0), (363608, 353807, 0)]
    x,y=list_tuple2xy(data)
    print(x,y)
    """
    col=len(data[0])#列数
    x = [tup[:col-1] for tup in data]
    y = [tup[col-1] for tup in data]
    return x,y


def list2txt(listname,dirname,filename,splstr='\t'):
    """
    功能：list->txt（列表三元组或列表存入文本文件）
    输入：[(0,2,3),(1,2,3)]或[0,1,2]
    输出：文本文件（在当前目录下创建）-分行输出-行形式 0    2   3或0
    返回：文件对象
    调用：
    list1=[(0,2,3),(1,2,3)]
    #list1=[1,2,3]
    list2txt(list1,"data/","list3file.txt")
    """
    # 先创建并打开一个文本文件
    # file=mkfile(dirname,filename)
    import os
    # 去除首位空格
    dirname = dirname.strip()
    # 去除尾部 \ 符号
    dirname = dirname.rstrip("\\")
    if dirname == "":
        return False
    # 判断路径是否存在
    # 存在     True
    # 不存在   False
    isExists = os.path.exists(dirname)
    # 判断结果
    if not isExists:
        # 如果不存在则创建目录
        # 　  # 创建目录操作函数
        os.makedirs(dirname)
        print(dirname + ' 创建成功')
        return True
    else:
        # 如果目录存在则不创建，并提示目录已存在
        print(dirname + ' 目录已存在,不再重复创建！')

    save_dir = Path(__file__).parent.absolute()  # 当前所在目录
    fdir = os.path.join(save_dir, dirname, filename)  # 连接目录
    file = open(fdir, 'w')
    print(fdir, "创建成功")

    for i in listname:
        # file.write(i + '\n')#原样写入-行形式(0, 2, 3)
        #实例判断-元素是否可迭代(list or tupel)
        if isinstance(i,list) or isinstance(i,tuple):
            templist = ''
            for j in i:#仅写入元素-行形式0	2	3
                templist+=str(j)+'\t'
                # templist.append(str(j)+splstr)#['0\t', '2\t', '3\t']
            file.write(str(templist) + '\n')
        else:
            file.write(str(i) + '\n')#每个元素分行写入
    # 注意关闭文件
    file.close()
    return file


def list2txt(l1,file_path):
    """
    功能：list中所有元素按行写入txt（每个元素一行）(最后一行不加换行符)；
    形式：一维[]->.txt
    调用：
        l1=['彪', '金玉', '王英', '迪雷', '沃洛韦齐区']
        list2txt(l1,'data/list1.txt')
    """
    with open(file_path, 'w',encoding='utf-8') as f:  #
        for i,line in enumerate(l1):#按行写入
            f.write(str(line))
            # 检查是否是最后一个元素
            if i < len(l1) - 1:
                # 如果不是最后一个元素，添加换行符
                f.write('\n')
        print("生成文件：",file_path)
# l1=['彪', '金玉', '王英', '迪雷', '沃洛韦齐区']
# list2txt(l1,'data/list1.txt')

def list2dim2txt(nearby_words_list,outfile_path,sep=' '):
    """功能：二维list生成txt文件；
    形式：[[]]->[str]->.txt
    调用：
        nearby_words_list=[['[CLS]', '吃', '海', '鲜', '时',',','[SEP]']]
        outfile_path='vocab_chi_syn.txt'
        list2dim2txt(nearby_words_list,outfile_path,sep=' ')
    """
    #1.[[]]->[str]
    nearby_str_list=[]
    # sep=' '
    for target1 in nearby_words_list:
        str3 = [str(i) for i in target1]  # list转换为str，用[for in];['1', '2', 'hello']
        str4 = sep.join(str3)  # join会去掉中间单引号；12hello
        nearby_str_list.append(str4)

    #2.[str]->txt
    with open(outfile_path, 'w',encoding='utf-8') as f:  #
        for line in nearby_str_list:  # 按行写入
            f.write(line + '\n')
        print("生成文件：", outfile_path)
# nearby_words_list=[['[CLS]', '吃', '海', '鲜', '时',',','[SEP]']]
# outfile_path='vocab_chi_syn.txt'
# list2dim2txt(nearby_words_list,outfile_path,sep=' ')


def list2dict(l1,l2):
    """l1为k，l2为v->{k,v}
    调用（中文词性编码字典）：
        pos_encode=['Ag','a','ad','an','b','c','dg','d','e','f','g','h','i','j','k','l','m','Ng','n','nr','ns','nt','nz',
            'o','p','q','r','s','tg','t','u','vg','v','vd','vn','w','x','y','z','un']#词性编码
        #中文词性名称
        pos_name=['形语素','形容词','副形词','名形词','区别词','连词','副语素','副词','叹词','方位词','语素','前接成分','成语',
                  '简称略语','后接成分。','习用语','数词','名语素','名词','人名','地名','机构团体','其他专名','拟声词','介词','量词',
                  '代词','处所词','时语素','时间词','助词','动语素','动词','副动词','名动词','标点符号','非语素字','语气词','状态词',
                  '未知词']
      print(list2dict(pos_encode,pos_name))
    """
    return {e:l2[i] for i,e in enumerate(l1)}


def list2tensor(l1:list,ty=float):
    """
    功能：将一维list转换为二维tensor-torch.Size([x, 1])
    参数：l1:[str]或[int]均可 ty：指定转换后的数据类型
    特殊：1个数一维也可以转为二维torch.Size([1, 1])
    调用：
    l1=['1','0']
    # l1 = [1, 0]
    l1=list2tensor(l1)
    print(l1,l1.shape,l1.dtype)#tensor([[1],[0]]) torch.Size([2, 1]) torch.int64
    """
    l1 = list(map(ty, l1))  # list内元素str变
    l2=[]
    for i in l1:
        l2.append([i])
    t4=tensor(l2)#由list创建tensor，默认为一维tensor([2, 3]) torch.Size([2])
    # t4=t4.view(-1,1)#变形-一维转二维（升维）
    # print(t4,t4.shape)
    # t4 = t4.reshape(-1, 1)  #
    return t4


def list_dict_count(list_dict:[{}],judgekey='correct',judgevalue=[true,false],countkey='topk_score_info',typename=1)->{}:
    """
    功能：统计括号内元素个数-按true和false；[{}]->{}
    参数：
        type=1  list中带括号的；（type=0  list中不带括号的）
    return：
       count_true,count_false如{'morphologic abnormality': 9, 'disorder': 1},{'disorder': 10}
    调用1：
    list_dict=[{
        "head": "Rheumatoid arthritis of bilateral elbows (disorder)",
        "relation": "is a (attribute)",
        "tail": "Rheumatoid arthritis of left elbow (disorder)",
        "pred_tail": "Arthritis of left elbow (disorder)",
        "pred_score": 0.6781,
        "topk_score_info": "{\"Arthritis of left elbow (disorder)\": 0.678, \"Arthritis of right elbow (disorder)\": 0.673}",
        "rank": 28,
        "correct": false
        },
        {
            "head": "Rheumatoid arthritis of bilateral elbows (disorder)",
            "relation": "associated morphology (attribute)",
            "tail": "Inflammation (morphologic abnormality)",
            "pred_tail": "Inflammation (morphologic abnormality)",
            "pred_score": 0.7369,
            "topk_score_info": "{\"Inflammation (morphologic abnormality)\": 0.737, \"Sclerosing inflammation (morphologic abnormality)\": 0.504}",
            "rank": 1,
            "correct": true
        }]
    count_true,count_false=list_dict_count(list_dict)
    print(f"count_true:{count_true}")
    print(f"count_false:{count_false}")
    """
    list_true=[]
    list_false=[]
    for d in list_dict:#处理[{}]
        if typename:#带括号的
            temp_list=str_getin_bracket(d[countkey])
        else:
            temp_list=str_repl_2list(d[countkey])[0]#形式(['Zu00fcrich'], ['0.691'])-取首元
        # print(temp_list)
        if d[judgekey]==judgevalue[0]:#true
            list_true=list_true+temp_list
        elif d[judgekey]==judgevalue[1]:#false
            list_false=list_false+temp_list
    count_true=list_count(list_true)
    count_false=list_count(list_false)
    return count_true,count_false


def json_load_path(dirname:str,filename:str):
    """
    参数：dir:上级目录下的目录名；filename:文件名
    功能：.json->对象；读取json文件为对象
    调用：
    dirname='data\list_dict_file'
    filename='eval_test.txt.json_forward_model_last.mdl.json'
    ld=json_load_path(dirname,filename)
    print(len(ld), type(ld), ld[0])  # 302345 <class 'list'>
    """
    import os
    from pathlib import Path
    abs_dir_par = Path(__file__).parent.parent.absolute()##取当前目录的上级目录
    dir_path=os.path.join(abs_dir_par,dirname,filename)
    print("加载json文件：",dir_path)
    with open(dir_path,'r',encoding='utf8') as fin:
        ld = json.load(fin)
    return ld


def list_dim_reduce(n:list)->list:
    """功能：list降维到一维-维度不均匀也适用
    调用：
    n = [[1, 2, 3], [7, [4], 3], [5, [8, [7]], 9]]
    list_dim_red(n)#[1, 2, 3, 7, 4, 3, 5, 8, 7, 9]
    """
    x = list(eval(str(n).replace("[", '').replace("]", '')))  # 维度均匀时也适用
    # print(x)
    return x


def list_dict2tensor(batch_data:[{}],key:str,typename=torch.float)->tensor:
    """
    功能：取list[dict]中指定键值为tensor；(注：键值为单值)
    形式：[{}]->tensor dtype=torch.float64
    key:字典的键值
    typename:torch.float，torch.double,torch.int,torch.long,torch.bool
    调用：
        batch_data1 = {'obj': 0, 'o_pre': 3}
        batch_data2 = {'obj': 2, 'o_pre': 2}
        batch_data = [batch_data1, batch_data2]
        print(list_dict2tensor(batch_data, key='obj',typename=torch.int64))
        #TypeError: tensor(): argument 'dtype' must be torch.dtype, not torch.tensortype
        #typename=torch.LongTensor改为torch.int64
    """

    # for ex in batch_data:
    #     print(ex,tensor(ex['o_pre'],dtype=typename))
    o_pre=tensor([ex[key] for ex in batch_data ],dtype=typename)
    # o_pre=(torch.LongTensor(ex['o_pre']) for ex in batch_data)
    # print(o_pre,o_pre.shape)
    return o_pre


def list_dict_mul2tensor(batch_data:[{}],key:str)->tensor:
    """
    功能：取list[dict]中指定键值为tensor；(注：键值非单个元素)
    形式：[{}]->tensor (tensor升维)
    key:字典的键值(注意：键值非单个元素)
    调用：
    t1=torch.rand(3,1)
    batch_data1 = {'obj': 0, 'o_pre': t1}
    batch_data2 = {'obj': 2, 'o_pre': t1}
    batch_data = [batch_data1, batch_data2]
    list_dict_mul2tensor(batch_data,key='o_pre')
    """
    l1=[]
    for ex in batch_data:
        l1.append(ex[key])
    # print(l1)
    l1=torch.stack(l1,dim=2)#list[tensor]升维
    # print(l1,l1.shape)
    return l1


def listdict_getv_bykey(ld,key)->[]:
    """[{key:value}]+key->[value]
    调用：
    ld=[{'text': 'payday', 'raw_ids': [24244]},{'text': 'do you have pets', 'raw_ids': [55, 40, 21, 6900]}]
    key='text'
    vlist=listdict_getv_bykey(ld,key)
    """
    vlist=[]
    for d in ld:
        # print(d)
        vlist.append(d[key])
    # print(vlist)
    return vlist


def list_len1_red_dim(list1:list):
    """
    功能：查看list所有元素长度与类型；
    调用：
    list1=[['a','b'],{'a':1}]
    list_len1_red_dim(list1)
    """
    while type(list1)==list and len(list1)==1:#多维长度为1的list-降维到一维
        list1=list1[0]
    else:
        if type(list1)==list:
            print("总长度为：len=",len(list1))
            for i in list1:
                print(f'len={len(i)}')
                if type(i) == list:
                    list_print(i)
                else:
                    print(type(i))


def list2str(target1:list,sep='')->str:
    """
    功能：一维list->str(可指定分隔符sep)（类型转换）
    调用：
        target1=[1,2,'hello']
        str4=list2str(target1)
        print(str4)
    """
    str3=[str(i) for i in target1]#list转换为str，用[for in];['1', '2', 'hello']
    str4=sep.join(str3)#join会去掉中间单引号；12hello
    return str4


def list_triple2txt(listname,dirname,filename,splstr='\t'):
    """
    功能：三元组list->txt（列表三元组或列表存入文本文件）
    输入：[(0,2,3),(1,2,3)]或[0,1,2]
    输出：文本文件（在当前目录下创建）-分行输出-行形式 0    2   3或0
    返回：文件对象
    调用：
    list1=[(0,2,3),(1,2,3)]
    #list1=[1,2,3]
    list2txt(list1,"data/","list3file.txt")
    """
    # 先创建并打开一个文本文件
    # file=mkfile(dirname,filename)
    import os
    # 去除首位空格
    dirname = dirname.strip()
    # 去除尾部 \ 符号
    dirname = dirname.rstrip("\\")
    if dirname == "":
        return False
    # 判断路径是否存在
    # 存在     True
    # 不存在   False
    isExists = os.path.exists(dirname)
    # 判断结果
    if not isExists:
        # 如果不存在则创建目录
        # 　  # 创建目录操作函数
        os.makedirs(dirname)
        print(dirname + ' 创建成功')
        return True
    else:
        # 如果目录存在则不创建，并提示目录已存在
        print(dirname + ' 目录已存在,不再重复创建！')

    save_dir = Path(__file__).parent.absolute()  # 当前所在目录
    fdir = os.path.join(save_dir, dirname, filename)  # 连接目录
    file = open(fdir, 'w')
    print(fdir, "创建成功")

    for i in listname:
        # file.write(i + '\n')#原样写入-行形式(0, 2, 3)
        #实例判断-元素是否可迭代(list or tupel)
        if isinstance(i,list) or isinstance(i,tuple):
            templist = ''
            for j in i:#仅写入元素-行形式0	2	3
                templist+=str(j)+'\t'
                # templist.append(str(j)+splstr)#['0\t', '2\t', '3\t']
            file.write(str(templist) + '\n')
        else:
            file.write(str(i) + '\n')#每个元素分行写入
    # 注意关闭文件
    file.close()
    return file


def list_to_txt(lst, file_path, separator='\n', keep_origin=True):
    """
    将列表写入文本文件
    :param lst: 要写入的列表
    :param file_path: 输出的文本文件路径
    :param separator: 列表元素分隔符，默认为换行符
    :param keep_origin: 是否保留原始格式（如保留列表的[]符号）（特点）
    """
    try:
        with open(file_path, 'w', encoding='utf-8') as f:
            if keep_origin:
                # 直接写入列表的字符串表示，保留[]符号
                f.write(str(lst))
            else:
                # 将列表元素用指定分隔符连接后写入文件
                f.write(separator.join(str(item) for item in lst))
        print(f"成功将列表写入 {file_path}")
    except Exception as e:
        print(f"写入文件时出错: {e}")

def txt_to_list(file_path, separator='\n', strip_whitespace=True, keep_origin=True):
    """
    从文本文件读取内容到列表
    :param file_path: 文本文件路径
    :param separator: 分隔符，默认为换行符
    :param strip_whitespace: 是否去除元素首尾空白字符
    :param keep_origin: 是否保留原始格式（如文件内容以[]开头结尾，则直接解析为Python列表）
    :return: 读取到的列表
    """
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            content = f.read()
            
            if keep_origin and content.strip().startswith('[') and content.strip().endswith(']'):
                # 尝试直接解析为Python列表
                try:
                    import ast
                    lst = ast.literal_eval(content.strip())
                    if not isinstance(lst, list):
                        raise ValueError("解析结果不是列表类型")
                except Exception as e:
                    print(f"以原始格式解析失败: {e}，将使用常规方式解析")
                    keep_origin = False
            
            if not keep_origin:
                if separator == '\n':
                    # 按行分割时直接使用splitlines()更安全
                    lst = content.splitlines()
                else:
                    lst = content.split(separator)
                
                if strip_whitespace:
                    lst = [item.strip() for item in lst]
                
        print(f"成功从 {file_path} 读取到列表")
        return lst
    except Exception as e:
        print(f"读取文件时出错: {e}")
        return []
    
def test_list_to_txt():
    # 使用示例
    my_list = ['第一行内容', '第二行内容', '第三行内容']
    list_to_txt(my_list, 'output.txt')  # 默认每行一个元素
    list_to_txt(my_list, 'output_comma.txt', separator=', ')  # 用逗号分隔
    list_to_txt(my_list, 'output_origin.txt', keep_origin=True)  # 保留原始格式



def test_txt_to_list():
    # 使用示例
    list_from_file = txt_to_list('output.txt')  # 默认按行读取
    print(list_from_file)

    comma_list = txt_to_list('output_comma.txt', separator=', ')  # 按逗号分隔读取
    print(comma_list)
    
    # 测试保留原始格式
    origin_list = txt_to_list('output_origin.txt', keep_origin=True)
    print(f"保留原始格式读取: {origin_list}")
    
    # 读取带有原始格式的示例文件
    try:
        contract_list = txt_to_list('input_files/list4page_contract.txt', keep_origin=True)
        print(f"合同文件读取结果类型: {type(contract_list)}")
        print(f"合同文件读取结果前20个字符: {str(contract_list)[:20]}")
    except Exception as e:
        print(f"读取合同文件时出错: {e}")
        

def list2txt(my_list,out_file):
    """功能：list加上换行符写入txt文件；
    调用：my_list = [1, "banana", "cherry"]
    list2txt(my_list,"data/list2.txt")
    """
    # 打开文件，以写入模式打开
    with open(out_file, "w",encoding='utf-8') as file:
        # 遍历列表中的每个元素，并将其写入文件中
        for item in my_list:
            item=str(item).strip()#去空
            file.write("%s\n" % item)
        print(f"{out_file}写入成功")


def listdim2lstr(l1,sep=' '):
    """
    功能：二维list降维一维;
    形式：[[]]->[str]（str指定分隔符sep）
    如：[['[CLS]', '吃', '海', '鲜', '时',',','[SEP]']] -> ['[CLS] 吃 海 鲜 时 , [SEP]']
    调用：
        l1=[['[CLS]', '吃', '海', '鲜', '时',',','[SEP]']]
        sep=' '
        l2=listdim2lstr(l1,sep=' ')
        print(l2)
    """
    l2=[]
    for target1 in l1:
        str3 = [str(i) for i in target1]  # list转换为str，用[for in];['1', '2', 'hello']
        str4 = sep.join(str3)  # join会去掉中间单引号；12hello
        l2.append(str4)
    return l2


def list_get_dict_value(l1:[],d1:dict)->list:
    """
    功能：一维tensor的dict取值；
    调用：
    l1=['0','01']
    d1={'0':'51','01':'52'}
    l1=list_get_dict_value(l1,d1)
    print(l1)
    """
    temp=[]
    for i in l1:
        # print(type(i))
        if type(i)==torch.Tensor:
            temp.append(d1[i.item()])
        else:
            temp.append(d1[i])
    return temp


def list2zip_tuple(s_name,r_name,o_name):
    """
    功能：三个list->[tuple]
    调用：
    s_name= ['Disorder  ','order']
    r_name= ['of bone','cb']
    o_name= ['(disorder)','cb']
    print(list2zip_tuple(s_name,r_name,o_name))
    """
    tri=list(zip(s_name,r_name,o_name))#TypeError: 'zip' object is not subscriptable
    return tri

def listuple_get_dictvalue(text,ent2id:{})->{}:
    """
    功能：[(s,r,o)]->data_dict[(s,r,o)]=(dict[s],dict[o])（不满足异构图形式的输入）
    调用：
    text=[('16028351000119100', 'associated_morphology_(attribute)', '80400009')]
    ent2id = {'16028351000119100': 0, '80400009': 1}
    data_dict=listuple_get_dictvalue(text,ent2id)
    # data_dict={('16028351000119100', 'associated_morphology_(attribute)', '80400009'): (tensor([0]), tensor([1]))}
    """
    data_dict = {}
    for i in range(len(text)):
        s,r,o=text[i]
        s_0id=ent2id[s]
        # r_0id = rel2id[s]
        o_0id = ent2id[o]
        data_dict[(s,r,o)]=(tensor([s_0id]),tensor([o_0id]))
    return data_dict


def list_minus_2set(d1:[],d2:[])-> {}:
    """
    功能(list的减法)：list1-list2>{}
    调用：
    d1=['025','35']
    d2=['025']
    set1=list_minus_2set(d1,d2)
    print(set1)
    """
    set1=set()
    for k in d1:
        set1.add(k)
    for k in d2:
        set1.remove(k)
    return set1


def list_ele_count_print(labels):
    """
    功能：统计tensor中的独立不重复元素并计数。
    #return_inverse: 是否返回原始tensor中的每个元素在这个无重复张量中的索引
    #return_counts: 统计原始张量中每个独立元素的个数
    调用：
    labels=[range(5)]
    list_ele_count(labels)
    """


    classes, idx, counts = torch.unique(torch.LongTensor(labels), return_inverse=True, return_counts=True)
    #tensor([0, 1, 2, 3, 4]) tensor([[0, 1, 2, 3, 4]]) tensor([1, 1, 1, 1, 1])
    print("labels:count",classes, idx, counts)
    # print(classes, idx, counts)
    num_per_class = torch.zeros_like(classes, dtype=torch.long)#同形状的0tensor
    # print(num_per_class)


def list_union_intersect(l1:list,l2:list)->list:
    """
    功能：2列表的交并集；
    返回：
        s:   并集
        s_inter:    交集
    调用：
    l1=['c','aaab','a']
    l2=['c']
    s,s_inter=list_union_intersect(l1,l2)
    print(s,s_inter)
    """
    s1=set(l1)
    s2=set(l2)
    s=s1.union(s2)
    s_inter=s1.intersection(s2)
    return list(s),list(s_inter)


def list_change_dim_test():
# labels=[range(5)]
# list_ele_count_print(labels)
    l=[]
    l1=['025','35']
    l.append(l1)#[]->[[],]
    l.append(l1)
    print(l)


def list_getby_index(l1:list,outcol:list)->[]:
    """[str]->[str]
    功能：单源list由下标取列表；
    调用：
     l1=['sentence\ttoken_type_ids\tthulac_word_length\tnlpir_word_length\thanlp_word_length\tlabel']
    outcol=[0,2,3,4,5]
    print(list_getby_index(l1,outcol))
    """
    l2=l1[0].split('\t')
    # print(l2)
    a=[l2[i] for i in outcol]
    # print(a)
    return a


def list_find_index(word_ids,element):
    """list->int
    功能:list中查找element的索引
    调用：
    word_ids=[(0, 1), (1, 2)]
    element=(1, 0)
    print(list_find_index(word_ids,element))
    """
    word_id = word_ids.index()#ValueError: (1, 1) is not in list
    # print(word_id)
    return word_id


def list2tsv(mylist,output):
    """功能：将二维list写入.tsv文件；
    调用：
    mylist = [['col1', 'col2', 'col3'], [1, 2, 3], [4, 5, 6], [7, 8, 9]]
    list2tsv(mylist,'output.tsv')
    """
    import csv
    # 将list写入tsv文件
    with open(output, 'w', newline='',encoding='utf-8') as outfile:
        writer = csv.writer(outfile, delimiter='\t')
        writer.writerows(mylist)
    print(f"{output}:生成成功！")
# mylist = [['col1', 'col2', 'col3'], [1, 2, 3], [4, 5, 6], [7, 8, 9]]
# list2tsv(mylist,'output.tsv')


def two_col2dict_vlist(indices):
    """#功能：同索引列值合并(用于稀疏矩阵)；
    方法：以行索引为键，列索引为值，同一行键的列索引放进一个list列表中
    形式：list(n,2)或tensor(n,2)->{行索引：[列值]}
    优点：可处理tensor和list；要求输入维度为(n,2);
    如：  indices =[[0, 1],[0, 2],[1, 2],[1, 3]]#（n,2）
            -》      {0: [1, 2], 1: [2, 3]}
    调用：
        # indices =[[0, 1],[0, 2],[1, 2],[1, 3]]
        result_dict=two_col2dict_vlist(indices)
        print(result_dict)
    """
    import torch
    indices=indices.tolist()  if type(indices)!=list else indices#-》list
    result_dict = {}
    for row, col in indices:
        # print(row,col)
        if row not in result_dict:
            result_dict[row] = [col]
        else:
            result_dict[row].append(col)

    # 打印结果
    # print(result_dict)
    return result_dict





# # indices =[[0, 1],[0, 2],[1, 2],[1, 3]]
# result_dict=two_col2dict_vlist(indices)
# print(result_dict)


# aggregator_types = ['concat']#6遍？
#     #总共训练6*5遍*epochs
# for t in aggregator_types:
#     print(t)

# l1=['[101', ' 2769', ' 2797', ' 3322', ' 696', ' 749', ' 8024',]
# l2=list2str(l1,sep='')
# l3=[101, 2769, 2797, 3322, 696, 749, 8024, 2769, 2682, 2940, 702, 2797, 3322, 511, 102, 2769, 2682, 743, 702, 3173, 2797, 3322, 8024, 3724, 2972, 5773, 511, 102]
# # print(len(l3))#28
# l4=[1, 1, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 1, 1]
# # print(sum(l4))#28
# # print(l2)
#
# # list_change_dim_test()
# # d2=['025']
# # l2=[["i'd", 'like', 'to', 'hear', 'some', 'trivia', 'about', 'florida'],
# #  ['please', 'tell', 'me', 'the', "bill's", 'minimum', 'payment']]
# # for l in l2:
# #     for i in l:
# #         print(i)
# l3=['please', 'tell', 'me', 'the', "bill's", 'minimum', 'payment']
# # print(l3[0:1])
# ld=[{'text': 'payday', 'raw_ids': [24244]},{'text': 'do you have pets', 'raw_ids': [55, 40, 21, 6900]}]
# key='text'
# l4=[1, 1, 2, 1, 1, 1]
# word_ids=[(0, 1), (1, 2)]
# element=(1, 0)
# l5='彪'
# l6=['金玉', '王英', '迪雷', '沃洛韦齐区']
# l7=[l5]+l6
# l8=[101, 6848, 2885, 4403, 3736, 5709, 1736, 4638, 1333, 1728, 2218, 3221, 3175, 912, 8024, 3300, 4510,
#     1220, 2820, 3461, 4684, 2970, 1168, 6809, 3862, 6804, 8024, 1453, 1741, 7623, 7667, 510, 7608, 2443,
#     510, 1555, 1767, 510, 6631, 2356, 510, 3033, 855, 671, 2418, 936, 1059, 511, 6983, 2421, 6163, 934, 671,
#     5663, 8024, 852, 6820, 5050, 3146, 3815, 120, 3807, 3737, 1762, 1920, 1828, 4638, 2238, 7553, 8024, 1728,
#     3634, 2523, 2207, 8024, 679, 6814, 1957, 1036, 948, 3221, 1599, 3614, 120, 1259, 4638, 3193, 7623, 3221,
#     6205, 2466, 4638, 8024, 6820, 5050, 705, 2168, 120, 3302, 1218, 1408, 8024, 671, 5663, 511, 102]
# print(len(l8))#len=106
# print(l7)
# for (word_idx, char_pos_pair) in enumerate(word_ids):
#     char_start, char_end = char_pos_pair
# print(1+l4)#TypeError: unsupported operand type(s) for +: 'int' and 'list'
# for i in (1,2):#tuple
#     print(i)
# print(l3[:10])

# print(list2str(l3,sep=' '))
# set1=list_minus_2set(d1,d2)
# print(set1)

# random.shuffle(l1)
# # print(l1)
# iter1=iter(l1)#list-><list_iterator object-特点：循环中可用next(terator)取下一个元素
# # print(iter1,next(iter1))
# l2=[1]
# l=[i for i in l1]
# # print(l)
# # l1=np.array(l1)
# # print(type(l1),l1[0].dtype)
# ld=[{'text':str,'raw_ids':[134,]}]
# for i in ld:
#     print(i)
# for i in enumerate(None):
#     print(i)
# print(torch.randperm(10))#tensor([8, 9, 1, 0, 2, 3, 5, 6, 4, 7])
# print(torch.zeros_like(10, dtype=torch.long))
# l=['0','1','2']
# str='\t'.join(l)
# print(str)

def list_raise_batch(str_list,batch_size):
    """list按批大小划分为n个list；
    形式：[]->[[]]；
    调用：
    str_list = ["str1", "str2", "str3","str4", "strN"]
    list_batch=list_raise_batch(str_list,3)
    print(list_batch)
    """
    # 计算需要提取的批次数量
    num_elements = len(str_list)
    num_batches = (num_elements - 1) // batch_size + 1  # 向上取整

    list_batch=[]
    # 提取每个批次的数据
    for i in range(num_batches):
        start_index = i * batch_size
        end_index = min((i + 1) * batch_size, num_elements)
        batch = str_list[start_index:end_index]
        list_batch.append(batch)
    return list_batch
# str_list = ["str1", "str2", "str3","str4", "strN"]
# list_batch=list_raise_batch(str_list,3)
# print(list_batch)


def enumerate_test():
    for i,t in enumerate([0,1]):#枚举i的值会保留
        print(i)
    print("all",i)